/* Gyroe 1.0.0 */
/* Backend.c : Modify the front-end's information for the backend.
   Compress SymTab, sline(trace info), and miscellaneous things, then
   send them to machine to start the execution. */

#include "euphoria.h"
#include "main-.h"
#include "sym_fields.h"

/* Poke4, Peek, and Peek4u were copied, not externed to give
   the backend all the speed possible. Backend will never send a
   bad struct, so all checks for bad structs are out.
   Peek couldn't be externed anyway, since it's all in the main
   execution loop. */

void Poke4(object a, object b)
{
    s1_ptr s1;
    object_ptr obj_ptr;
    object buffer;

    /* determine the address to be poked */
    if (IS_ATOM_INT(a)) {
        poke4_addr = (unsigned long *)a;
    }
    else if (IS_ATOM(a)) {
        poke4_addr = (unsigned long *)(unsigned long)(DBL_PTR(a)->dbl);
    }

    /* look at the value to be poked */
    if (IS_ATOM_INT(b)) {
        *poke4_addr = (unsigned long)b;
    }
    else if (IS_ATOM(b)) {
        temp_dbl = DBL_PTR(b)->dbl;
        *poke4_addr = (unsigned long)temp_dbl;
    }
    else {
        /* second arg is sequence */
        s1 = SEQ_PTR(b);
        obj_ptr = s1->base;
        while (1) {
            buffer = *(++obj_ptr);
            if (IS_ATOM_INT(buffer)) {
               *poke4_addr++ = (unsigned long)buffer;
            }
            else if (IS_ATOM(buffer)) {
                if (buffer == NOVALUE)
                    break;
                temp_dbl = DBL_PTR(buffer)->dbl;
                *poke4_addr++ = (unsigned long)temp_dbl;
            }
        }
    }
}

void Poke(object a, object b) {
    s1_ptr s1;
    object_ptr obj_ptr;
    object buffer;

    if (IS_ATOM_INT(a)) {
        poke_addr = (unsigned char *)a;
    }
    else {
        poke_addr = (unsigned char *)(unsigned long)(DBL_PTR(a)->dbl);
    }
    if (IS_ATOM_INT(b)) {
        *poke_addr = (unsigned char)b;
    }
    else if (IS_ATOM(b)) {
        *poke_addr = (signed char)DBL_PTR(b)->dbl;
    }
    else {
        s1 = SEQ_PTR(b);
        obj_ptr = s1->base;
        while (1) {
            buffer = *(++obj_ptr);
            if (IS_ATOM_INT(buffer)) {
                *poke_addr++ = (unsigned char)buffer;
            }
            else if (IS_ATOM(buffer)) {
                if (buffer == NOVALUE)
                    break;
                *poke_addr++ = (signed char)DBL_PTR(buffer)->dbl;
            }
        }
    }
}

object Peek4u(object a)
{
    object top;

    /* check address */
    if (IS_ATOM_INT(a)) {
	peek4_addr = (unsigned long *)a;
    }
    else if (IS_ATOM(a)) {
	peek4_addr = (unsigned long *)(unsigned long)(DBL_PTR(a)->dbl);
    }

    top = (object)*peek4_addr;

    /* backend's version is always unsigned. */
    if ((unsigned)top > (unsigned)MAXINT)
        top = NewDouble((double)(unsigned long)top);

    return top;
}

object di_add(object a, int b)
/* Return a value of a plus b. a could be double, which means
   that we have to find the value in the struct. If not, it's
   int and we can return a normal add. */
{
    if (IS_ATOM_INT(a))
        return a + b;
    else
        return NewDouble(DBL_PTR(a)->dbl + b);
}

void _17BackEnd()
{
    int addr = 0, e_addr = 0, entry = 0, entry_addr = 0, fn = 0;
    int l_addr = 0, lit = 0, lit_string = 0, no_name = 0;
    int ms = 0, other_strings = 0, sl = 0;
    int src = 0, string_size = 0;
    int repcount, size, _short, st;
    int _4121 = 0, _4128 = 0, _4134 = 0, _4194 = 0;
    int _1, _2;
    int i, j;
    object_ptr Base;
    s1_ptr ptr;

    size = (SEQ_PTR(_4SymTab)->length + 1) * 52;
    st = _7allocate(size); /* SymTab */
    memory_set(st, 0, size); /* All fields are 0 (NULL) by default */

    Poke4(st, SEQ_PTR(_4SymTab)->length);

    RefDS(_39);
    lit_string = _39; /* Literals are stored in a string. */

    addr = di_add(st, 52); /* addr = st + ST_ENTRY_SIZE */

    for (i = 1; i <= SEQ_PTR(_4SymTab)->length;i++) {
        ptr = SEQ_PTR(_4SymTab);
        Base = ptr->base + i;

        entry = *Base;
        RefDS(entry);

        ptr = SEQ_PTR(*Base);
        Base = ptr->base;

        Poke4(di_add(addr, 4), Base[S_NEXT]);
        Poke4(di_add(addr, 8), Base[S_MODE]);
        Poke4(di_add(addr, 9), Base[S_SCOPE]);

        /* length(entry) >= S_NAME and sequence(entry[S_NAME]) */
        if (SEQ_PTR(entry)->length >= 7 && IS_SEQUENCE(Base[7])) {
            Poke4(di_add(addr, 10), Base[S_FILE_NO]); /* entry[S_FILE_NO] */
            Poke4(di_add(addr, 16), Base[S_TOKEN]); /* entry[S_TOKEN] */
            string_size += SEQ_PTR(Base[S_NAME])->length+1; /* length(entry[S_NAME])+1 */
        }

        if (Base[S_MODE] == 1) { /* entry[S_MODE] = M_NORMAL */
            /* entry[S_TOKEN] = PROC or FUNC or TYPE */
            if (Base[S_TOKEN] == 27 || Base[S_TOKEN] == 501 || Base[S_TOKEN] == 504) {
                if (IS_SEQUENCE(Base[S_CODE])) { /* entry[S_CODE] */
                    e_addr = _7allocate(4+4*SEQ_PTR(Base[S_CODE])->length);
                    /* allocate(4+4*length(entry[S_CODE])) */

                    Poke4(e_addr, SEQ_PTR(Base[S_CODE])->length); /* length(entry[S_CODE]) */
                    Poke4(di_add(e_addr, 4), Base[S_CODE]); /* entry[S_CODE] */
                    Poke4(di_add(addr, 20), e_addr);
                }
                Poke4(di_add(addr, 28), Base[S_FIRSTLINE]); /* entry[S_FIRSTLINE] */
                Poke4(di_add(addr, 32), Base[S_TEMPS]); /* entry[S_TEMPS] */
                Poke4(di_add(addr, 36), Base[S_NUM_ARGS]); /* entry[S_NUM_ARGS] */
                Poke4(di_add(addr, 48), Base[S_STACK_SPACE]); /* entry[S_STACK_SPACE] */
            }
        }
        /* length(entry) >= S_TOKEN and entry[S_TOKEN] = NAMESPACE or
           length(entry) < S_NAME and entry[S_MODE] = M_CONSTANT */
        else if ((SEQ_PTR(entry)->length >= S_TOKEN && Base[S_TOKEN] == 523) ||
                 (SEQ_PTR(entry)->length < S_NAME && Base[S_MODE] == 2)) {
            Poke4(addr, SEQ_PTR(lit_string)->length); /* Offset */
            Concat((object_ptr)&lit_string, lit_string, (s1_ptr)(_16compress(Base[S_OBJ])));
            /* Literal is stored in S_OBJ. Compress, then store. */
        }

        addr = di_add(addr, 52);
        /* addr += ST_ENTRY_SIZE */
    }

    /* Save the literals. */
    lit = _7allocate(SEQ_PTR(lit_string)->length);
    Poke(lit, lit_string);

    /* Dump lit_string, not going to need it anymore. */
    RefDS(_39);
    DeRef(lit_string);
    lit_string = _39;

    addr = _7allocate(di_add(string_size, 1));

    Ref(st);
    DeRef(entry_addr);
    entry_addr = st;

    no_name = NewString("<no-name>");

    /* Symbols to C strings... */
    for (i = 1;i <= SEQ_PTR(_4SymTab)->length;i++) {
        ptr = SEQ_PTR(_4SymTab);
        Base = ptr->base;
        entry = Base[i];
        RefDS(entry);

        ptr = SEQ_PTR(entry);
        Base = ptr->base;

        entry_addr = di_add(entry_addr, 52);

        if (SEQ_PTR(entry)->length >= S_NAME) { /* length(entry) >= S_NAME */
            if (IS_SEQUENCE(Base[S_NAME])) { /* sequence(entry[S_NAME]) */
                Poke4(di_add(entry_addr, 12), addr);
                Poke(addr, Base[S_NAME]);
                addr = di_add(addr, SEQ_PTR(Base[S_NAME])->length);
                Poke(addr, 0); /* 0 terminate the string. */

                addr = di_add(addr, 1);

                if (Base[S_TOKEN] == 523) { /* entry[S_TOKEN] = NAMESPACE */
                    /* poke4(entry_addr, peek4u(entry_addr)+lit) */
                    Poke4(entry_addr, di_add(Peek4u(entry_addr), lit));
                }
            }
            else {
                Poke4(di_add(entry_addr, 12), no_name);
            }
        }
        else if (Base[S_MODE] == 2) { /* entry[S_MODE] = M_CONSTANT */
            /* poke4(entry_addr, peek4u(entry_addr)+lit) */
            Poke4(entry_addr, di_add(Peek4u(entry_addr), lit));
        }
    }

    /* Symtab's no longer needed, so free it. */
    DeRef(_4SymTab);
    size = 0;

    /* Determine the size to allocate. Shouldn't overflow int. */
    for (i = 1;i <= SEQ_PTR(_4slist)->length;i++) {
        ptr = SEQ_PTR(_4slist);
        Base = ptr->base;

        if (IS_SEQUENCE(Base[i]))
            /* Add one for sequences. */
            size++;
        else
            /* Add the number inside this point. */
            size += Base[i];
    }

    sl = _7allocate((size+1)*8);
    memory_set(sl, 0, (size+1)*8);
    Poke4(sl, size);

    addr = di_add(sl, 8); /* 0 position is ignored. Start with 1. */
    string_size = 0;

    for (i = 1;i <= SEQ_PTR(_4slist)->length;i++) {
        ptr = SEQ_PTR(_4slist);
        Base = ptr->base;

        if (IS_SEQUENCE(Base[i])) {
            entry = Base[i];
            repcount = 1;
            ptr = SEQ_PTR(entry);
            /* Set ptr now or we'll have to set ptr after the loop,
               which is redundant if else gets hit. */
        }
        else {
            entry = Base[i-1];
            ptr = SEQ_PTR(entry);

            if (ptr->length < 4)
                ptr->base[1] = di_add(ptr->base[1], 1); /* slist[i][1] */
            else
                ptr->base[2] = di_add(ptr->base[2], 1); /* slist[i][2] */
            repcount = Base[i]; /* slist[i] */
        }
        Base = ptr->base;
        _short = ptr->length < 4;

        for (j = 1;j <= repcount;j++) {
            Poke4(di_add(addr, 4), Base[2-_short]); /* entry[LINE-short] */
            Poke(di_add(addr, 6), Base[3-_short]); /* entry[LOCAL_FILE_NO-_short] */

            if (_short == 0) {
                if (Base[1]) {
                    /* all_source[1+floor(entry[SRC]/SOURCE_CHUNK)] + remainder(entry[SRC]/SOURCE_CHUNK) */
                    _4134 = SEQ_PTR(_11all_source)->base[1 + (Base[1]/10000)] + (Base[1] % 10000);
                    Poke4(addr, _4134);
                }
                Poke(di_add(addr, 7), Base[4]); /* entry[OPTIONS] */
            }
            addr = di_add(addr, 8);
            Base[2-_short] = di_add(Base[2-_short], 1); /* entry[LINE-short] += 1 */
        }
    }

    /* We're done with slist now. */
    RefDS(_39);
    DeRef(_4slist);
    _4slist = _39;

    /* other_strings = append(file_name, file_name_entered) & warning_list */
    RefDS(_4file_name_entered);
    Append(&_4121, _4file_name, _4file_name_entered);
    Concat((object_ptr)&other_strings, _4121, (s1_ptr)_6warning_list);

    ptr = SEQ_PTR(other_strings);
    Base = ptr->base;

    string_size = ptr->length;
    /* Start with length of other_strings. */

    for (i = 1; i <= SEQ_PTR(other_strings)->length;i++) {
        /* Get the length of the stuff in other_strings. */
        ptr = SEQ_PTR(Base[i]);
        string_size += ptr->length;
    }

    ms = _7allocate(4*(10+SEQ_PTR(other_strings)->length));
    /* Miscellaneous. */

    Poke4(ms, _15max_stack_per_call);
    Poke4(di_add(ms, 4), _11AnyTimeProfile);
    Poke4(di_add(ms, 8), _11AnyStatementProfile);
    Poke4(di_add(ms, 12), _15sample_size);
    Poke4(di_add(ms, 16), _4gline_number);
    Poke4(di_add(ms, 20), 0); /* il_file (Always 0) */
    Poke4(di_add(ms, 24), SEQ_PTR(_6warning_list)->length);
    Poke4(di_add(ms, 28), SEQ_PTR(_4file_name)->length);

    fn = _7allocate(string_size);

    ptr = SEQ_PTR(other_strings);
    Base = ptr->base;

    for (i = 1;i <= ptr->length;i++) {
        /* Poke4(ms+32+(i-1)*4), fn */
        Poke4(di_add(ms, 32+(i-1)*4), fn);
        Poke(fn, Base[i]);

        fn = di_add(fn, SEQ_PTR(Base[i])->length);
        Poke(fn, 0); /* 0 terminate the string. */
        fn = di_add(fn, 1);
    }

    DeRef(addr);
    DeRef(e_addr);
    DeRef(fn);
    DeRef(no_name);
    DeRef(l_addr);
    DeRef(other_strings);
    DeRef(src);

    DeRef(_4121);
    DeRef(_4134);
    DeRef(_4194);

    _1 = NewS1(3);
    _2 = (int)((s1_ptr)_1)->base;
    *((int *)(_2+4)) = st; /* SymTab */
    *((int *)(_2+8)) = sl; /* Sline (trace) */
    *((int *)(_2+12)) = ms; /* miscellaneous */
    _4128 = MAKE_SEQ(_1);
    machine(65, _4128); /* Starts the backend */
}

